using System.Collections.Generic;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Content.Pipeline;
using Microsoft.Xna.Framework.Content.Pipeline.Graphics;
using Microsoft.Xna.Framework.Content.Pipeline.Processors;
using SolarWinds.Helpers;

namespace SolarWindsContentPipelineExtension
{
    [ContentProcessor]
    public class BoundingBoxModelProcessor : ModelProcessor
    {
        private readonly List<BoundingBox> mBoxes = new List<BoundingBox>();
        private readonly List<List<Vector3>> mMeshVerts = new List<List<Vector3>>();
        private readonly List<Vector3> mAllPositions = new List<Vector3>();


        // Bounding Box's
        private void CheckNode(NodeContent nodeContent)
        {
            foreach (NodeContent child in nodeContent.Children)
            {
                ProcessNodeContent(child);
            }
        }

        private void ProcessNodeContent(NodeContent nodeContent)
        {
            var meshContent = nodeContent as MeshContent;

            if (meshContent != null)
            {
                // Get VertData
                GetAllVerticies(meshContent);

                var min = new Vector3(float.MaxValue);
                var max = new Vector3(float.MinValue);

                foreach (Vector3 position in meshContent.Positions)
                {
                    if (position.X < min.X) min.X = position.X;
                    if (position.Y < min.Y) min.Y = position.Y;
                    if (position.Z < min.Z) min.Z = position.Z;

                    if (position.X > max.X) max.X = position.X;
                    if (position.Y > max.Y) max.Y = position.Y;
                    if (position.Z > max.Z) max.Z = position.Z;
                }

                mBoxes.Add(new BoundingBox(min, max));
            }
            else
            {
                CheckNode(nodeContent);
            }
        }

        // Vertex positions
        private void GetAllVerticies(MeshContent mesh)
        {
            for (int g = 0; g < mesh.Geometry.Count; g++)
            {
                GeometryContent geometry = mesh.Geometry[g];

                var temp = new List<Vector3>();

                for (int ind = 0; ind < geometry.Indices.Count; ind++)
                {
                    // Transforms all of my verticies to local space.
                    //Vector3 position = Vector3.Transform(geometry.Vertices.Positions[geometry.Indices[ind]], mesh.AbsoluteTransform);
                    Vector3 position = geometry.Vertices.Positions[geometry.Indices[ind]];

                    temp.Add(position);
                    mAllPositions.Add(position);
                }

                mMeshVerts.Add(temp);
            }
        }

        // Tangents.
        private static void GenerateTangents(NodeContent input, ContentProcessorContext context)
        {
            var mesh = input as MeshContent;

            if (mesh != null)
            {
                MeshHelper.CalculateTangentFrames(mesh,
                    VertexChannelNames.TextureCoordinate(0),
                    VertexChannelNames.Tangent(0),
                    VertexChannelNames.Binormal(0));
            }

            foreach (NodeContent child in input.Children)
            {
                GenerateTangents(child, context);
            }
        }
        // Normals
        private static void GenerateNormals(NodeContent input, ContentProcessorContext context)
        {
            var mesh = input as MeshContent;

            if (mesh != null)
            {
                MeshHelper.CalculateNormals(mesh, true);
            }

            foreach (NodeContent child in input.Children)
            {
                GenerateNormals(child, context);
            }
        }

        public override ModelContent Process(NodeContent input, ContentProcessorContext context)
        {
            // Calculate Mesh Tangents.
            //GenerateNormals(input, context);

            // Calculate Mesh Normals.
            //GenerateTangents(input, context);

            // Setup bounding box data.
            ProcessNodeContent(input);

            var modelData = new object[4];

            modelData[0] = BoundingBox.CreateFromPoints(mAllPositions);
            modelData[1] = BoundingSphere.CreateFromPoints(mAllPositions);
            modelData[2] = mBoxes;
            modelData[3] = mMeshVerts;

            ModelContent basemodel = base.Process(input, context);
            basemodel.Tag = modelData;
            return basemodel;
        }
    }
}
